-
Notifications
You must be signed in to change notification settings - Fork 11
Allow futures independent of wasi pollables to progress #70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow futures independent of wasi pollables to progress #70
Conversation
This commit moves away from using noop waker for the main task. Instead, it keeps track if the main task is ready to progress again immediately after `Future::poll()`. If it is, the runtime will skip calling `wasi::io::poll::poll` if there are no wasi pollables scheduled, or otherwise call in a non-blocking manner to quickly get an update for all wasi pollables before progressing the main task again.
Thanks for this bug report and PR. I started reviewing this but ended up deciding on a simpler design which doesn't create an immediately-ready pollable, and instead tracks the main tasks readiness internally. I re-used your test in #70 but rewrote the rest of this PR. |
Thanks for the review @pchickey,
I did debate whether to just keep polling until the main task eventually returns Pending without being woken, then only poll for the wasi resources. But then thought won't that actually stall wasi dependent futures if something is always waking the main task up? And while writing this, I realized one scenario that is valid and also cause wasi futures to stall. let cpu_intensive_task = async {
let work_units = 100_000_000;
for i in 0..work_units {
if i % 100 == 0 {
futures_lite::future::yield_now().await;
}
// run some cpu intensive processing
}
} |
Thanks for this bug report and PR. I just merged #71 which fixes the same underlying bug, but has a different implementation which is more efficient. So, I will close this PR now. |
The So, even if we don't use wasi:io/poll.poll to block until a wasi pollable is ready, we are still checking if an external event has made it ready as part of each block_on loop. |
Hmm, it seems that this check is only done once on the initial poll, before registering a waker with the reactor. From my understanding with Rust futures, futures doesn't get polled again when it returns On the same line of logic, it's not guaranteed that an AsyncPollable
|
In the above code, every call of
My reading of the rust docs is that this is phrased with "should", and because wasip2 is a different sort of operating system than Unix, it is reasonable to break that rule:
The implementation of poll does return quickly, and does not block - |
I don't think we can break that rule, because some crates (like async-task / futures-concurrency) do expect futures to be only re-polled when their wakers wake, not always. #[test]
fn cooperative_concurrency() {
crate::runtime::block_on(async {
let cpu_heavy = async move {
// Simulating a CPU-heavy task that runs for 10 second and yields occasionally
for _ in 0..100 {
std::thread::sleep(std::time::Duration::from_millis(100));
futures_lite::future::yield_now().await;
}
true
};
let timeout = async move {
crate::time::Timer::after(crate::time::Duration::from_secs(3))
.wait()
.await;
false
};
let mut future_group = futures_concurrency::future::FutureGroup::<
Pin<Box<dyn std::future::Future<Output = bool>>>,
>::new();
future_group.insert(Box::pin(cpu_heavy));
future_group.insert(Box::pin(timeout));
let result = future_group.next().await;
assert_eq!(result, Some(false), "cpu_heavy task should have timed out");
});
} Looking inside the
You are right, I was totally unaware of |
as provided by @SilverMira in #70, and tracked in #73 Co-authored-by: SilverMira <[email protected]>
as provided by @SilverMira in #70, and tracked in #73 Co-authored-by: SilverMira <[email protected]>
Fixes #69
This commit moves away from using noop waker for the main task. Instead, it keeps track if the main task is ready to progress again immediately after
Future::poll()
.If it is, the runtime will skip calling
wasi::io::poll::poll
if there are no wasi pollables scheduled, or otherwise call in a non-blocking manner to quickly get an update for all wasi pollables before progressing the main task again.This is my first time contributing to an async runtime, so feel free to point out any mistakes